#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

struct msg{
	unsigned int tid;
	int data;
	struct msg* next;
};

struct msg* msg_queue = NULL;

pthread_mutex_t qlock;
pthread_cond_t qready;

struct msg* new_msg(void)
{
	struct msg* ms;
	ms = malloc(sizeof(struct msg));
	if(NULL == ms)
		return NULL;

	ms->next = NULL;

	return ms;
}

void enqueue_msg(struct msg* ms)
{
	pthread_mutex_lock(&qlock);
	ms->next = msg_queue; 
	msg_queue = ms; 
	pthread_mutex_unlock(&qlock);
	pthread_cond_signal(&qready);
}

void* create_queue_msg(void* arg)
{
	unsigned int tid;
	int i;	
	struct msg* p;

	tid = (unsigned int)pthread_self();
	printf("create_queue_msg:thread[%u] start\n", tid);

	for(i = 0; i < 5; i++)
	{
		p = new_msg();
		if(NULL == p)
		{
			printf("thread[%u]: num [%d] new_msg failed\n", tid, i);
			continue;
		}
		p->data = i;
		p->tid = tid;
		enqueue_msg(p);
	}

	printf("create_queue_msg:thread[%u] end\n", tid);

	pthread_exit((void*)0);
}

void* handle_queue_msg(void *arg)
{
	struct msg* p;

	printf("handle_queue_msg:start\n");
	
	pthread_cleanup_push(pthread_mutex_unlock, &qlock);

	while(1)
	{
		pthread_mutex_lock(&qlock);	
		while(NULL == msg_queue)
		{
			pthread_testcancel();
			pthread_cond_wait(&qready, &qlock);
		}

		p = msg_queue;	
		msg_queue = p->next;
		pthread_mutex_unlock(&qlock);	

		printf("handle_msg:tid[%u]data[%d]\n", p->tid, p->data);
		free(p);
	}

	printf("handle_queue_msg: end\n");
	pthread_cleanup_pop(0);
	pthread_exit((void*) 0);
}

int main(int argc, char **argv)
{
	pthread_t threads[5];
	pthread_t id_handle;

	void* ret;
	int err;
	int i;
	
	pthread_mutex_init(&qlock, NULL);
	pthread_cond_init(&qready, NULL);

	err = pthread_create(&id_handle, NULL, handle_queue_msg, NULL);
	if(err)
	{
		printf("cannot create handle_queue_msg: %s\n", strerror(err));
	}

	for(i = 0; i< 5; i++)
	{
		err = pthread_create(&threads[i], NULL, create_queue_msg, NULL);
		if(err)
		{
			printf("cannot create 'creat_queue_msg' thread[%d]: %s\n", i, strerror(err));
		}
	}

	

	for(i = 0; i< 5; i++)
	{
		err = pthread_join(threads[i], &ret);
		if(err)
		{
			printf("cannot join with threads[%d]: %s\n", i,  strerror(err));
		}
		printf("join, create_queue_msg threads[%d] exit cod: %d\n", i,  (int)ret);
	}

	err = pthread_cancel(id_handle);
	if(err)
	{
		printf("cannot cancel handle_queue_msg: %s\n", strerror(err));
	}
	
	err = pthread_join(id_handle, &ret);
	if(err)
	{
		printf("cannot join with thr_2: %s\n", strerror(err));
	}
	printf("join, handle_queue_msg exit cod: %d\n", (int)ret);
	

	pthread_mutex_destroy(&qlock);
	pthread_cond_destroy(&qready);

	printf("main end\n");
	return 0;
}

